例外處理是一種處理程式執行期間可能發生的錯誤或異常情況的機制,允許開發者以結構化和可控的方式處理錯誤。
例外處理在 Java 中的重要性體現在以下幾個方面:
Java 的例外處理機制建立在一個層次分明的類別階層上。理解這個階層結構對於有效使用例外處理至關重要。
在 Java 中,所有的例外都源自 java.lang.Throwable
類別。Throwable 是例外階層的根,它有兩個主要的子類別:Error 和 Exception。
try-catch 語句是 Java 例外處理的核心機制,它允許我們捕獲和處理程式執行過程中可能發生的例外。
try-catch 的基本結構如下:
try {
// 可能拋出例外的程式碼
} catch (ExceptionType e) {
// 處理特定類型例外的程式碼
}
當不同類型的例外需要不同的處理方式時,可以使用多重 catch 區塊:
try {
// 可能拋出多種例外的程式碼
} catch (IOException e) {
// 處理 IOException
} catch (SQLException e) {
// 處理 SQLException
} catch (Exception e) {
// 處理其他所有類型的例外
}
注意:更具體的例外類型應該放在前面的 catch 區塊中。
finally 區塊用於定義無論是否發生例外都需要執行的程式碼:
try {
// 可能拋出例外的程式碼
} catch (Exception e) {
// 處理例外
} finally {
// 無論是否發生例外都會執行的程式碼
// 通常用於清理資源
}
public class ExceptionExample {
public static void main(String[] args) {
try {
int result = divide(10, 0);
System.out.println("結果:" + result);
} catch (ArithmeticException e) {
System.out.println("發生算術錯誤:" + e.getMessage());
} finally {
System.out.println("程式執行完畢");
}
}
public static int divide(int a, int b) {
return a / b;
}
}
在這個例子中,我們嘗試除以零,這會拋出 ArithmeticException。try-catch 區塊捕獲了這個例外並進行了處理,而 finally 區塊確保了無論發生什麼情況,都會執行最後的清理工作。
例外處理機制中,throw
和 throws
是兩個重要的關鍵字,用於不同的目的。
throw
關鍵字用於在程式中主動拋出一個例外。當遇到無法處理的情況時,我們可以使用 throw
來創建並拋出一個例外物件。
語法:
throw new ExceptionType("錯誤訊息");
throws
關鍵字用在方法宣告中,表示該方法可能拋出某些類型的例外,而這些例外需要調用者來處理。
語法:
public void someMethod() throws ExceptionType1, ExceptionType2 {
// 方法內容
}
public class ThrowThrowsExample {
public static void main(String[] args) {
try {
validateAge(15);
} catch (IllegalArgumentException e) {
System.out.println("年齡驗證失敗:" + e.getMessage());
}
try {
readFile("nonexistent.txt");
} catch (IOException e) {
System.out.println("檔案讀取失敗:" + e.getMessage());
}
}
public static void validateAge(int age) {
if (age < 18) {
throw new IllegalArgumentException("年齡必須大於或等於 18");
}
System.out.println("年齡驗證通過");
}
public static void readFile(String filename) throws IOException {
// 模擬檔案讀取操作
throw new IOException("檔案 " + filename + " 不存在");
}
}
在這個例子中,validateAge
方法使用 throw
來拋出一個 IllegalArgumentException
,而 readFile
方法使用 throws
來宣告它可能拋出 IOException
。
正確使用 throw
和 throws
可以幫助我們更好地控制程式的流程,並確保例外能夠在適當的地方被處理。
雖然 Java 提供豐富的內建例外類別,但有時我們需要創建自定義例外來更好地表達特定的錯誤情況。自定義例外可以提供更多的上下文資訊,使錯誤處理更加精確和有意義。
創建自定義例外非常簡單,只需要繼承 Exception
類別(對於受檢例外)或 RuntimeException
類別(對於非受檢例外)。
public class InsufficientFundsException extends Exception {
private double amount;
public InsufficientFundsException(double amount) {
super("餘額不足,缺少 " + amount + " 元");
this.amount = amount;
}
public double getAmount() {
return amount;
}
}
自定義例外適用於以下情況:
public class BankAccount {
private double balance;
public BankAccount(double initialBalance) {
this.balance = initialBalance;
}
public void withdraw(double amount) throws InsufficientFundsException {
if (amount > balance) {
double shortfall = amount - balance;
throw new InsufficientFundsException(shortfall);
}
balance -= amount;
}
public static void main(String[] args) {
BankAccount account = new BankAccount(100);
try {
account.withdraw(150);
} catch (InsufficientFundsException e) {
System.out.println(e.getMessage());
System.out.println("缺少金額:" + e.getAmount());
}
}
}
在這個例子中,我們創建一個 InsufficientFundsException
來處理銀行帳戶餘額不足的情況。這個自定義例外包含錯誤訊息、缺少的金額資訊,這樣可以使錯誤處理更加具體和方便除錯。
try-with-resources 是 Java 7 引入的一個強大特性,提供一種更簡潔、更安全的方式來處理需要關閉的資源,如檔案、資料庫連接等。
try-with-resources 語句會自動關閉在括號中宣告的資源,無論 try 區塊是正常結束還是拋出例外。這大大簡化資源管理,並有助於防止資源洩漏。
基本語法:
try (Resource resource = new Resource()) {
// 使用資源的程式碼
} catch (Exception e) {
// 處理例外
}
要使用 try-with-resources,資源類別必須實現 AutoCloseable
介面。這個介面只有一個方法:close()
。
public class MyResource implements AutoCloseable {
public void doSomething() {
System.out.println("使用資源");
}
@Override
public void close() throws Exception {
System.out.println("關閉資源");
}
}
public class TryWithResourcesExample {
public static void main(String[] args) {
try (MyResource resource = new MyResource()) {
resource.doSomething();
throw new RuntimeException("測試例外");
} catch (Exception e) {
System.out.println("捕獲例外: " + e.getMessage());
}
}
}
在這個例子中,即使拋出例外,MyResource
的 close()
方法也會被自動調用。這確保了資源總是被正確關閉,無論程式執行過程中是否發生錯誤。
try-with-resources 語句大大簡化資源管理的程式碼,並提高程式的可靠性,適用於處理檔案、網絡連接、資料庫連接等需要顯式關閉的資源。
有效的例外處理能提高程式的穩定性,以及改善程式的可讀性和可維護性。以下是一些例外處理的實踐:
try {
// 可能拋出 IOException 的程式碼
} catch (IOException e) {
logger.error("檔案操作失敗", e);
// 適當的錯誤處理
}
使用日誌系統記錄例外資訊,包括例外類型、訊息和堆疊追蹤,對於後續的調試和問題分析非常重要。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(YourClass.class);
try {
// 可能拋出例外的程式碼
} catch (Exception e) {
logger.error("操作失敗", e);
}
避免捕獲例外後不做任何處理。如果確實不需要處理,至少要記錄例外資訊。
try {
// 程式碼
} catch (Exception e) {
// 不要這樣做
// e.printStackTrace();
// 應該這樣做
logger.error("發生錯誤", e);
// 可能的錯誤恢復邏輯
}
當捕獲低層級例外時,考慮將其轉換為有意義的高層級例外,有助於保持 API 的一致性和可理解性。
public void processFile(String filename) throws FileProcessingException {
try {
// 檔案處理邏輯
} catch (IOException e) {
throw new FileProcessingException("處理檔案 " + filename + " 時發生錯誤", e);
}
}
本篇文章同步刊載: JYI.TW
筆者個人的網站: JUNYI